.pc = * "Utility Functions"

.label zero_page_start = 64
.label zero_page_end = 128

//////////////////////////////

stack_base: .byte 0

zero_page_save:
    .fill [zero_page_end-zero_page_start],0
    
init_zero_page:
{
    // copy the first 6 bytes by hand, because MemCpy uses them
    ldx #5
loop:
    lda zero_page_start,x
    sta zero_page_save,x
    dex
    bpl loop

    // copy the rest
    :MemcpySlow( zero_page_start+6, zero_page_save+6,
             [zero_page_end-zero_page_start-6] )
    rts
}
    
restore_zero_page:
{
    :MemcpySlow( zero_page_save+6, zero_page_start+6,
             [zero_page_end-zero_page_start-6] )
    ldx #5
loop:
    lda zero_page_save,x
    sta zero_page_start,x
    dex
    bpl loop
    
    rts
}

//////////////////////////////
    
.macro MemcpySlow(src, dst, bytes)
{
    .label srcPtr = zero_page_start
    .label dstPtr = srcPtr + 2
    .label count = dstPtr + 2

    // store the parameters in these variables
    lda #<bytes
    sta count
    lda #>bytes
    sta count+1

    lda #<src
    sta srcPtr
    lda #>src
    sta srcPtr+1

    lda #<dst
    sta dstPtr
    lda #>dst
    sta dstPtr+1
    
    // call the method
    jsr memcpy_slow
}

memcpy_slow:
{
    .label srcPtr = zero_page_start
    .label dstPtr = srcPtr + 2
    .label count = dstPtr + 2

    ldy #0

loop:    
    // if count == 0, return
    lda count
    bne copy_one
    lda count+1
    bne copy_one
    rts

copy_one:
    // copy a byte
    lda (srcPtr),y
    sta (dstPtr),y

    // update pointers, counter
    :Inc16(srcPtr)
    :Inc16(dstPtr)
    :Dec16(count)
    jmp loop
}

//////////////////////////////
    
.macro Inc16(ptr)
{
    inc ptr
    bne done
    inc ptr+1
done:   //nop
}

.macro Dec16(ptr)
{
    lda ptr
    bne no_borrow
    dec ptr+1
no_borrow:
    dec ptr
}

//////////////////////////////

.macro Add16(ptr1, ptr2, ptrDst)
{
    lda ptr1
    clc
    adc ptr2
    sta ptrDst
    lda ptr1+1
    adc ptr2+1
    sta ptrDst+1
}
    
//////////////////////////////

// parameters 2 and 3 are X-indexed.
.macro Add16_X(ptr1, ptr2, ptrDst)
{
    lda ptr1
    clc
    adc ptr2,x
    sta ptrDst,x
    lda ptr1+1
    adc ptr2+1,x
    sta ptrDst+1,x
}

//////////////////////////////

.macro Set16(ptr, literal)
{
    lda #<literal
    sta ptr
    lda #>literal
    sta ptr+1
}

//////////////////////////////

.macro AddLit8To16(ptr, literal)
{
    lda #literal
    clc
    adc ptr
    sta ptr
    bcc noCarry
    inc ptr+1
noCarry:
}
    
//////////////////////////////

.macro LoadFile(fname, addr)
{
    ldx #<load_addr
    ldy #>load_addr
    jsr load_file
    jmp end
    
load_addr:
    .word addr
filename_length:
    .byte [end-filename]
filename:
    .text fname
end:
}

load_file:
{
    .label data_ptr = zero_page_start // two bytes
    .label y_tmp = zero_page_start + 2
    
    stx data_ptr
    sty data_ptr+1

    // increment the XY pointer by 3
    txa
    clc
    adc #3
    tax
    tya
    adc #0
    sta y_tmp

    // load the filename length into A
    ldy #2
    lda (data_ptr),y
    ldy y_tmp
    
    jsr $ffbd // call SETNAM routine
    
    lda #1 // file number 1 (arbitrary)
    ldx $ba // device number (use last used device)
    ldy #0 // secondary address -- means we will specify the load address
    jsr $ffba // call SETLFS routine

    // put load address in XY
    ldy #0
    lda (data_ptr),y
    tax
    iny
    lda (data_ptr),y
    tay
    lda #0 // specify "load" rather than "verify"
    jsr $ffd5 // call LOAD routine

    bcc success

    // set error code to border color and loop
    sta $d020
    jmp *
    
success:    
    rts
}

//////////////////////////////

.const BORDER_TIMERS = false

.macro SetBorder(color)
{
    .if (BORDER_TIMERS)
    {
        lda #color
        sta $d020
    }
}

.macro SaveBorder()
{
    .if (BORDER_TIMERS)
    {
        lda $d020
        pha
    }
}

.macro RestoreBorder()
{
    .if (BORDER_TIMERS)
    {
        pla
        sta $d020
    }
}

//////////////////////////////

.macro RasterWait(line)
{
    lda #line
loop:
    cmp $d012
    bne loop
}

.macro Nops(num)
{
    .for (var i = 0; i < num; i++)
    {
        nop
    }
}
